package impl.io;

import impl.core.UBox;
import impl.util.UDecideLog;
import impl.util.UtilityAssertion;
import impl.util.messages.ExceptionMessages;

import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.OWLXMLOntologyFormat;
import org.semanticweb.owlapi.io.RDFXMLOntologyFormat;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationValue;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyFormat;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
import org.semanticweb.owlapi.vocab.PrefixOWLOntologyFormat;

import exceptions.UDecideParseClassExpressionException;
import exceptions.UDecideParseManchesterSyntaxException;
import exceptions.UDecideParseUtilityValueException;
import exceptions.UDecideUBoxLoadException;
import exceptions.UDecideUBoxSaveException;

public class UDecideIO {

	private final static String	ontIRIString				= "http://dbpedia.org/";
	private final static String	uFragment					= "utility";
	private final static String	manSyntaxFragment			= "manSyntax";
	private final static String	uDecideTmpClassIdentifier	= "uDecideTmpClass";

	public static String		folderPath;

	public enum OntologyFormat {
		RDFXML, OWLXML
	}

	public static Collection<UtilityAssertion> loadUtilityAssertionsFromFile(File file) throws UDecideUBoxLoadException {
		OWLOntologyManager man = OWLManager.createOWLOntologyManager();
		OWLOntology uBoxOnt;

		try {
			uBoxOnt = man.loadOntologyFromOntologyDocument(file);
		} catch (OWLOntologyCreationException e) {
			throw new UDecideUBoxLoadException(e.getMessage());
		}

		LinkedList<UtilityAssertion> utilityAssertions = new LinkedList<UtilityAssertion>();

		for (OWLEquivalentClassesAxiom a : uBoxOnt.getAxioms(AxiomType.EQUIVALENT_CLASSES)) {
			for (OWLClass namedClass : a.getNamedClasses()) {
				if (namedClass.getIRI().toString().contains(uDecideTmpClassIdentifier)) {
					try {
						double utilityValue = getUtilityValue(a);
						String manSyntax = getManchesterSyntax(a);

						OWLClassExpression expr = getClExFromUBoxClassExpressionAxiom(a);

						UtilityAssertion utiliyAssertion = new UtilityAssertion(manSyntax, utilityValue, expr);

						utilityAssertions.add(utiliyAssertion);
					} catch (UDecideParseUtilityValueException | UDecideParseManchesterSyntaxException | UDecideParseClassExpressionException ex) {
						UDecideLog.warning(ex.toString());
					}
				}
			}

		}

		return utilityAssertions;
	}

	public static void saveUBoxToFile(UBox uBox, File file, OntologyFormat format) throws UDecideUBoxSaveException {
		OWLOntologyManager man = OWLManager.createOWLOntologyManager();
		OWLOntology uBoxOnt = null;
		try {
			uBoxOnt = man.createOntology(IRI.create(ontIRIString));
		} catch (OWLOntologyCreationException e) {
			throw new UDecideUBoxSaveException(e.getMessage());
		}

		OWLDataFactory df = man.getOWLDataFactory();

		int i = 0;

		for (UtilityAssertion u : uBox.getUtilityAssertions()) {
			// create equi class axiom to save class expression of ubox
			OWLClass dummyClass = df.getOWLClass(IRI.create(uBoxOnt.getOntologyID().getOntologyIRI() + uDecideTmpClassIdentifier + (i++)));
			OWLAxiom equiAxiom = df.getOWLEquivalentClassesAxiom(dummyClass, u.getClassExpression());

			// annotate axiom with utility value and manchester syntax of class
			// expression

			OWLAnnotation utilityValueAnnotation = df.getOWLAnnotation(df.getOWLAnnotationProperty(IRI.create(ontIRIString + "#" + uFragment)),
					df.getOWLLiteral(u.getUtilityValue()));
			OWLAnnotation manchesterSyntaxTextAnnotation = df.getOWLAnnotation(df.getOWLAnnotationProperty(IRI.create(ontIRIString + "#" + manSyntaxFragment)),
					df.getOWLLiteral(u.getManSyntax()));

			HashSet<OWLAnnotation> annotSet = new HashSet<OWLAnnotation>();
			annotSet.add(utilityValueAnnotation);
			annotSet.add(manchesterSyntaxTextAnnotation);

			equiAxiom = equiAxiom.getAnnotatedAxiom(annotSet);

			man.addAxiom(uBoxOnt, equiAxiom);
		}

		switch (format) {
			case RDFXML:
				saveOntology(man, uBoxOnt, file, new RDFXMLOntologyFormat());
				return;
			case OWLXML:
				saveOntology(man, uBoxOnt, file, new OWLXMLOntologyFormat());
				return;
				// case MANCHESTERSYNTAX:
				// saveOntology(ont, file, new
				// ManchesterSyntaxDocumentFormat());
				// return;
			default:
				saveOntology(man, uBoxOnt, file, new OWLXMLOntologyFormat());
				return;
		}
	}

	private static void saveOntology(OWLOntologyManager man, OWLOntology ont, File file, OWLOntologyFormat format) {

		if (format.isPrefixOWLOntologyFormat()) {
			OWLOntologyFormat ontFormat = man.getOntologyFormat(ont);
			if (ontFormat.isPrefixOWLOntologyFormat()) {
				((PrefixOWLOntologyFormat) format).copyPrefixesFrom(ontFormat.asPrefixOWLOntologyFormat());
			}
		}

		try {
			man.saveOntology(ont, format, IRI.create(file.toURI()));
		} catch (OWLOntologyStorageException e) {
			UDecideLog.error(ExceptionMessages.UBOX_SAVE_EXCEPTION.toString() + e.getMessage());
		}
	}

	private static Double getUtilityValue(OWLAxiom axiom) throws UDecideParseUtilityValueException {
		for (OWLAnnotation annotation : axiom.getAnnotations()) {
			if (annotation.getProperty().getIRI().getFragment().toString().equals(uFragment)) {
				OWLAnnotationValue annValue = annotation.getValue();
				OWLLiteral literalValue = (OWLLiteral) annValue;
				try {
					return literalValue.parseDouble();
				} catch (NumberFormatException ex) {
					throw new UDecideParseUtilityValueException();
				}
			}
		}

		throw new UDecideParseUtilityValueException();
	}

	private static String getManchesterSyntax(OWLAxiom axiom) throws UDecideParseManchesterSyntaxException {
		for (OWLAnnotation annotation : axiom.getAnnotations()) {
			if (annotation.getProperty().getIRI().getFragment().toString().equals(manSyntaxFragment)) {
				OWLAnnotationValue annValue = annotation.getValue();
				if (annValue instanceof OWLLiteral) {
					OWLLiteral literalValue = (OWLLiteral) annValue;
					return literalValue.getLiteral();
				}
			}
		}

		throw new UDecideParseManchesterSyntaxException();
	}

	/**
	 * Returns the first anonymous class expression, i.e. the first class expression that is not a named class contained in the OWLEquivalentClassesAxiom.
	 *
	 * @param axiom
	 * @return
	 * @throws UDecideParseClassExpressionException
	 */
	private static OWLClassExpression getClExFromUBoxClassExpressionAxiom(OWLEquivalentClassesAxiom axiom) throws UDecideParseClassExpressionException {
		for (OWLClassExpression expr : axiom.getClassExpressions()) {
			if (expr.isAnonymous()) {
				return expr;
			}
		}

		throw new UDecideParseClassExpressionException();
	}
}
